package com.myzl.coal.client;

import com.myzl.coal.client.channel.init.*;
import com.myzl.common.enums.DeviceProtocolEnum;
import io.netty.bootstrap.Bootstrap;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioDatagramChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.util.NettyRuntime;
import io.netty.util.ResourceLeakDetector;
import lombok.extern.slf4j.Slf4j;

import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

/**
 * @author 91941
 */
@Slf4j
public class NettyBootStrap {

    /**
     * 线程组
     */
    //表示服务器连接监听线程组，专门接受 accept 新的客户端client 连接
    private static NioEventLoopGroup NIO_GROUP;
    private static Map<Integer, List<EventLoopGroup>> eventLoopGroupMap = new ConcurrentHashMap<>();

    static {
        NIO_GROUP = new NioEventLoopGroup(NettyRuntime.availableProcessors());
    }


    public static void udp(int port, ChannelInitializer channelInitializer) {
        log.info("-------------------------------udpServer-------------------------");
        try {
            //1，创建netty bootstrap 启动类
            Bootstrap serverBootstrap = new Bootstrap();
            NioEventLoopGroup eventLoopGroup = new NioEventLoopGroup(NettyRuntime.availableProcessors());
            //2、设置boostrap 的eventLoopGroup线程组
            serverBootstrap = serverBootstrap.group(eventLoopGroup)
            //3、设置NIO UDP连接通道
            .channel(NioDatagramChannel.class)
            //4、设置通道参数 SO_BROADCAST广播形式
            .option(ChannelOption.SO_BROADCAST, true)
            //5、设置处理类 装配流水线
            .handler(channelInitializer);
            ResourceLeakDetector.setLevel(ResourceLeakDetector.Level.ADVANCED);
            //6、绑定server，通过调用sync（）方法异步阻塞，直到绑定成功
            ChannelFuture future = serverBootstrap.bind(port).sync();
            //7、监听通道关闭事件，应用程序会一直等待，直到channel关闭
//            f.channel().closeFuture().sync();
            future.addListener(new ChannelFutureListener() {
                @Override
                public void operationComplete(ChannelFuture channelFuture) throws Exception {
                    if (channelFuture.isSuccess()) {
                        eventLoopGroupMap.put(port, Arrays.asList(eventLoopGroup));
                        log.info("udpServer success,port:{},", channelFuture.channel().localAddress().toString());
                    } else {
                        log.info("udpServer fail,port:" + port, channelFuture.cause());
                    }
                }
            });

        } catch (Exception e) {
            log.info("异常 port:" + port, e);
        } finally {
            // 退出，释放线程资源
//            bossLoopGroup.shutdownGracefully();
        }
    }

    public static void tcpClient(String url, int port, ChannelInitializer channelInitializer) {
        try {
            Bootstrap bootstrap = new Bootstrap();
            bootstrap
                    .group(NIO_GROUP)
                    .channel(NioSocketChannel.class)
                    .option(ChannelOption.TCP_NODELAY, true)
                    .option(ChannelOption.SO_KEEPALIVE, false)
                    .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 15000)
                    .handler(channelInitializer);
            ResourceLeakDetector.setLevel(ResourceLeakDetector.Level.ADVANCED);
            SocketAddress socketAddress = new InetSocketAddress(url, port);
            ChannelFuture cf = bootstrap.remoteAddress(socketAddress).connect();

            cf.addListener((ChannelFutureListener) future -> {
                if (!future.isSuccess()) {
                    //重连交给后端线程执行
                    future.channel().eventLoop().schedule(() -> {
                        log.info("重连服务端...ip为：{},端口为：{},channel:{}", url, port, future.channel().hashCode());
                        try {
                            tcpClient(url, port, channelInitializer);
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }, 10, TimeUnit.SECONDS);
                } else {
//                    BaseHandler.URL_PIPELINE.put(BaseHandler.getKey(url, port), future.channel().pipeline());
//                    log.info(future.channel().pipeline().toString());

                    log.info("ip:{},port:{},服务端连接成功...", url, port);
                }
            });
        } catch (Exception e) {
            log.info("异常：", e);
        } finally {
            // 退出，释放线程资源
            //            bossLoopGroup.shutdownGracefully();
        }
    }

    public static void tcpServer(int port, ChannelInitializer channelInitializer) {
        EventLoopGroup boss = new NioEventLoopGroup(2);
        EventLoopGroup worker = new NioEventLoopGroup();
        ServerBootstrap bootstrap = new ServerBootstrap();
        try {
            bootstrap.group(boss, worker)
                    .channel(NioServerSocketChannel.class)
                    .option(ChannelOption.SO_BACKLOG, 128)
                    // 使消息立即发出去，不用等待到一定的数据量才发出去
                    .option(ChannelOption.TCP_NODELAY, true)
                    // 保持长连接状态
                    .childOption(ChannelOption.SO_KEEPALIVE, false)
                    .childHandler(channelInitializer);
            ResourceLeakDetector.setLevel(ResourceLeakDetector.Level.ADVANCED);
            // 绑定端口，同步等待成功
            ChannelFuture f = bootstrap.bind(port).sync();
            if (f.isSuccess()) {
                eventLoopGroupMap.put(port, Arrays.asList(boss, worker));
                log.info("Netty Start successful");
            } else {
                log.error("Netty Start failed");
            }
            // 等待服务监听端口关闭
//            ChannelFuture closeFuture = f.channel().closeFuture().sync();
//            closeFuture.addListener((ChannelFutureListener) channelFuture -> {
//                if (channelFuture.isSuccess()) {
//                    log.info("tcpServer success,port:{},", channelFuture.channel().localAddress());
//                } else {
//                    log.info("tcpServer fail,port:" + port, channelFuture.cause());
//                }
//            });
        } catch (Exception e) {
            log.info("异常：", e);
        } finally {
            // 退出，释放线程资源
//            worker.shutdownGracefully();
//            boss.shutdownGracefully();
        }
    }

    public static ChannelInitializer getHandler(int deviceProtocol){
        ChannelInitializer channelInitializer;
        switch (DeviceProtocolEnum.getValue(deviceProtocol)) {
            case MODBUS:
                channelInitializer=new ModbusChannelInitializer();
                break;
            case JSON:
                channelInitializer=new JsonChannelInitializer();
                break;
            case LIDAR:
                channelInitializer=new LidarChannelInitializer();
                break;
            case WEBSOCKET:
                channelInitializer=new WebSocketChannelInitializer();
                break;
            case TEST:
                channelInitializer=new TestChannelInitializer();
                break;
            default:
                channelInitializer=new StringChannelInitializer();
        }
        return channelInitializer;
    }

    public static void close(int port) {
        if (eventLoopGroupMap.containsKey(port)) {
            List<EventLoopGroup> eventLoopGroups = eventLoopGroupMap.get(port);
            eventLoopGroups.forEach(EventLoopGroup::shutdownGracefully);
        }
    }


}
